#include "StdAfx.h"
#include "SkyDome.h"

using namespace std;

namespace FEngine
{
	SkyDome::SkyDome(const string& _fileName, float _rotationSpeed)
		: Node(), mRotateSpeed(_rotationSpeed)
	{
		mNumberPrimitives	= 0;
		mNumberVertices		= 0;
		mVertexBuffer		= NULL;
		mIndexBuffer		= NULL;
		mCubeMapTexture		= NULL;

		_createSphere(_fileName);
	}

	SkyDome::~SkyDome()
	{
		if (mVertexBuffer)
			mVertexBuffer->Release();
		if (mVertexDeclaration)
			mVertexDeclaration->Release();
		if (mIndexBuffer)
			mIndexBuffer->Release();
		if (mCubeMapTexture)
			mCubeMapTexture->Release();
	}

	void SkyDome::_createSphere(const string& _fileName)
	{
		// Generate the vertices
		vector<Vector3> verticesVector;
		double angle = 0;
		double x, y;
		int subivisions_longitude = 8;
		int subivisions_latitude = 4;
		float rad;
		float _radius = 1.0f;
		float height = _radius;
		float firstCircleHeight = ((_radius * 2) / (subivisions_latitude - 1)) / 4;
		
		// Top point of the sphere
		verticesVector.push_back(Vector3(0, _radius, 0));

		// Top circle of the sphere
		height = _radius - firstCircleHeight;
		for (int j = 0; j < subivisions_longitude; ++j)
		{
			angle = j * 360.0f / subivisions_longitude;
			rad = sqrt((_radius * _radius) - (height * height));
			x = cos(DEGREES_TO_RADIANS(angle)) * rad;
			y = sin(DEGREES_TO_RADIANS(angle)) * rad;
			verticesVector.push_back(Vector3((float)x, height, (float)y));
		}
		
		height = _radius;
		for (int i = 1; i < subivisions_latitude - 1; ++i)
		{
			height -= (_radius * 2) / (subivisions_latitude - 1);

			// Create one circle on the Z plane
			for (int j = 0; j < subivisions_longitude; ++j)
			{
				angle = j * 360.0f / subivisions_longitude;
				rad = sqrt((_radius * _radius) - (height * height));
				x = cos(DEGREES_TO_RADIANS(angle)) * rad;
				y = sin(DEGREES_TO_RADIANS(angle)) * rad;
				verticesVector.push_back(Vector3((float)x, height, (float)y));
			}
		}

		// Bottom circle of the sphere
		height = firstCircleHeight - _radius;
		for (int j = 0; j < subivisions_longitude; ++j)
		{
			angle = j * 360.0f / subivisions_longitude;
			rad = sqrt((_radius * _radius) - (height * height));
			x = cos(DEGREES_TO_RADIANS(angle)) * rad;
			y = sin(DEGREES_TO_RADIANS(angle)) * rad;
			verticesVector.push_back(Vector3((float)x, height, (float)y));
		}

		// Bottom point of the sphere
		verticesVector.push_back(Vector3(0, -_radius, 0));

		
		// Generate the indexes
		vector<unsigned int> indexesVector;
		int offset;
		
		// Link the top point with the first circle
		for (int i = 1; i < subivisions_longitude + 1; ++i)
		{
			indexesVector.push_back(0);

			// Link the last point of the circle to the first
			if (i == subivisions_longitude)
				indexesVector.push_back(1);
			else
				indexesVector.push_back(i + 1);

			indexesVector.push_back(i);
		}
		
		// Link the bottom point with the last circle
		offset = verticesVector.size() - subivisions_longitude - 1;
		for (int i = 0; i < subivisions_longitude; ++i)
		{
			indexesVector.push_back(offset + i);

			// Link the last point of the circle to the first
			if (i + 1 == subivisions_longitude)
				indexesVector.push_back(offset);
			else
				indexesVector.push_back(offset + i + 1);
			
			indexesVector.push_back(verticesVector.size() - 1);
		}
		
		// Link every circle to the circle placed under
		offset = 1;
		for (int i = 1; i < subivisions_latitude; ++i)
		{
			for (int j = 0; j < subivisions_longitude; ++j)
			{
				// First triangle
				indexesVector.push_back(offset + j);
				
				// Link the last point of the circle to the first
				if (j + 1 == subivisions_longitude)
					indexesVector.push_back(offset + subivisions_longitude);
				else
					indexesVector.push_back(offset + subivisions_longitude + j + 1);
				
				indexesVector.push_back(offset + subivisions_longitude + j);
				
				// Second triangle
				indexesVector.push_back(offset + j);
				
				// Link the last point of the circle to the first
				if (j + 1 == subivisions_longitude)
				{
					indexesVector.push_back(offset + j - subivisions_longitude + 1);
					indexesVector.push_back(offset + j + 1);
				}
				else
				{
					indexesVector.push_back(offset + j + 1);
					indexesVector.push_back(offset + subivisions_longitude + j + 1);
				}
			}
			offset += subivisions_longitude;
		}


		// Create the vertex and index buffers
		
		// Get the vertices
		FE_VERTEX* vertices = new FE_VERTEX[verticesVector.size()];
		for (unsigned int n = 0; n < verticesVector.size(); ++n)
			vertices[n].POSITION = verticesVector[n];
		mNumberVertices = verticesVector.size();
		unsigned int verticesSize = mNumberVertices * sizeof(FE_VERTEX);
		
		// Get the indexes
		short* indexes = new short[indexesVector.size()];
		for (unsigned int n = 0; n < indexesVector.size(); ++n)
		{
			indexes[n] = (short)indexesVector[n];
		}
		
		mNumberPrimitives = indexesVector.size() / 3;
		unsigned int indexesSize = indexesVector.size() * sizeof(short);

		// Create the vertex buffer
		CoreEngine::getInstance()->getDeviceInterface()->CreateVertexBuffer(verticesSize, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &mVertexBuffer, NULL);

		// Create the index buffer
		CoreEngine::getInstance()->getDeviceInterface()->CreateIndexBuffer(indexesSize, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &mIndexBuffer, NULL);

		VOID* pVoid;

		// Copy the vertex buffer data into memory
		mVertexBuffer->Lock(0, 0, (void**)&pVoid, 0);
		memcpy(pVoid, vertices, verticesSize);
		mVertexBuffer->Unlock();
		
		// Copy the index buffer data into memory
		mIndexBuffer->Lock(0, 0, (void**)&pVoid, 0);
		memcpy(pVoid, indexes, indexesSize);
		mIndexBuffer->Unlock();

		// Texture
		setTexture(_fileName);
		
		// Load the vertex declaration
		CoreEngine::getInstance()->getDeviceInterface()->CreateVertexDeclaration(VertexDeclaration, &mVertexDeclaration);
	}
	
	void SkyDome::setTexture(const std::string& _fileName)
	{
		if (mCubeMapTexture)
			mCubeMapTexture->Release();

		std::wstring stemp = std::wstring(_fileName.begin(), _fileName.end());
		LPCWSTR fileName = stemp.c_str();
		if (D3DXCreateCubeTextureFromFile(CoreEngine::getInstance()->getDeviceInterface(), fileName, &mCubeMapTexture) != D3D_OK)
		{
			MessageBox(NULL, L"Erreur lors du chargement des textures", L"Error", 0);
			throw FEngineException("SkyDome", "setTexture", "Error during the loading of the texture");
		}
	}
	
	void SkyDome::setRotateSpeed(float _speed)
	{
		mRotateSpeed = _speed;
	}
	
	void SkyDome::update(double _elapsedTime)
	{
		Node::update(_elapsedTime);
		
		if (CoreEngine::getInstance()->getCamera())
			setPosition(CoreEngine::getInstance()->getCamera()->getPosition());
		
		if (mRotateSpeed)
			rotate(0, 0, (float)DEGREES_TO_RADIANS(mRotateSpeed * _elapsedTime / 1000));
	}

	void SkyDome::render(double _elapsedTime)
	{
		Node::render(_elapsedTime);
		
		LPD3DXEFFECT shader = CoreEngine::getInstance()->getShaderEffect();
		IDirect3DDevice9* device = CoreEngine::getInstance()->getDeviceInterface();

		D3DXMATRIX spaceMat = _getWorldMatrix();
		D3DXMATRIX matWVP = spaceMat * CoreEngine::getInstance()->getCamera()->_getMatrixView() * CoreEngine::getInstance()->getCamera()->_getMatrixProjection();

		if (shader->SetTechnique(shader->GetTechniqueByName("SkyDome")) != D3D_OK)
			throw FEngineException("SkyDome", "render", "Unknown shader technique");
		if (shader->SetMatrix(shader->GetParameterByName(NULL, "gWorldViewProj"), &matWVP) != D3D_OK)
			throw FEngineException("SkyDome", "render", "Unknown shader parameter");
		if (shader->SetTexture(shader->GetParameterByName(NULL, "gEnvMap"), mCubeMapTexture) != D3D_OK)
			throw FEngineException("SkyDome", "render", "Unknown shader parameter");
		shader->CommitChanges();
		
		device->SetVertexDeclaration(mVertexDeclaration);
		device->SetStreamSource(0, mVertexBuffer, 0, sizeof(FE_VERTEX));
		device->SetIndices(mIndexBuffer);
		
		unsigned int cPasses, iPass;
		shader->Begin(&cPasses, 0);
		for (iPass= 0; iPass< cPasses; ++iPass)
		{
			shader->BeginPass(iPass);
			CoreEngine::getInstance()->getDeviceInterface()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mNumberVertices, 0, mNumberPrimitives);
			shader->EndPass();
		}
		shader->End();
	}

} // nemespace FEngine
